home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-01-17 | 53.3 KB | 1,372 lines | [TEXT/R*ch] |
- Archive-name: C++-faq/part7
- Posting-Frequency: monthly
- Last-modified: Jan 1, 1997
- URL: http://www.cerfnet.com/~mpcline/c++-faq-lite/
-
- AUTHOR: Marshall Cline / cline@parashift.com / Paradigm Shift, Inc. /
- One Park St. / Norwood, NY 13668 / 315-353-6100 (voice) / 315-353-6110 (fax)
-
- COPYRIGHT: This posting is part of "C++ FAQ Lite." The entire "C++ FAQ Lite"
- document is Copyright(C) 1991-96 Marshall P. Cline, Ph.D., cline@parashift.com.
- All rights reserved. Copying is permitted only under designated situations.
- For details, see section [1].
-
- NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR PROVIDES NO
- WARRANTY WHATSOEVER, EITHER EXPRESS OR IMPLIED, REGARDING THE WORK, INCLUDING
- WARRANTIES WITH RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
- PURPOSE.
-
- C++-FAQ-Lite != C++-FAQ-Book: This document, C++ FAQ Lite, is not the same as
- the C++ FAQ Book. The book (C++ FAQs, Cline and Lomow, Addison-Wesley) is 500%
- larger than this document, and is available in bookstores. For details, see
- section [3].
-
- ==============================================================================
-
- SECTION [29]: How to mix C and C++
-
-
- [29.1] What do I need to know when mixing C and C++ code?
-
- There are several caveats:
- * Your must use your C++ compiler when compiling main() (e.g., for static
- initialization)
- * Your C++ compiler should direct the linking process (e.g., so it can get its
- special libraries)
- * Your C and C++ compilers probably need to come from same vendor and have
- compatible versions (e.g., so they have the same calling conventions)
-
- In addition, you'll need to read the rest of this section to find out how to
- make your C functions callable by C++ and/or your C++ functions callable by C.
-
- ==============================================================================
-
- [29.2] How can I include a standard C header file in my C++ code?
-
- To #include a standard header file (such as <stdio.h>), you don't have to do
- anything unusual. E.g.,
-
- // This is C++ code
-
- #include <stdio.h> // Note: nothing unusual in #include line
-
- main()
- {
- printf("Hello world\n"); // Note: nothing unusual in the call
- }
-
- Note: Somewhat different guidelines apply for non-system C headers. There are
- two cases: either you can't change the header[29.3], or you can change the
- header[29.4].
-
- ==============================================================================
-
- [29.3] How can I include a non-system C header file in my C++ code?
-
- If you are including a C header file that isn't provided by the system, you may
- need to wrap the #include line in an extern C { /*...*/ } construct. This
- tells the C++ compiler that the functions declared in the header file are are C
- functions.
-
- // This is C++ code
-
- extern "C" {
- // Get declaration for f(int i, char c, float x)
- #include "my-C-code.h"
- }
-
- main()
- {
- f(7, 'x', 3.14); // Note: nothing unusual in the call
- }
-
- Note: Somewhat different guidelines apply for C headers provided by the system
- (such as <stdio.h>)[29.2] and for C headers that you can change[29.4].
-
- ==============================================================================
-
- [29.4] How can I modify my own C header files so it's easier to #include them
- in C++ code?
-
- If you are including a C header file that isn't provided by the system, and if
- you are able to change the C header, you should strongly consider adding the
- extern C {...} logic inside the header to make it easier for C++ users to
- #include it into their C++ code. Since a C compiler won't understand the
- extern C construct, you must wrap the extern C { and } lines in an #ifdef so
- they won't be seen by normal C compilers.
-
- Step #1: Put the following lines at the very top of your C header file (note:
- the symbol __cplusplus is #defined if/only-if the compiler is a C++ compiler):
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- Step #2: Put the following lines at the very bottom of your C header file:
-
- #ifdef __cplusplus
- }
- #endif
-
- Now you can #include your C header without any extern C nonsense in your C++
- code:
-
- // This is C++ code
-
- // Get declaration for f(int i, char c, float x)
- #include "my-C-code.h" // Note: nothing unusual in #include line
-
- main()
- {
- f(7, 'x', 3.14); // Note: nothing unusual in the call
- }
-
- Note: Somewhat different guidelines apply for C headers provided by the system
- (such as <stdio.h>)[29.2] and for C headers that you can't change[29.3].
-
- ==============================================================================
-
- [29.5] How can I call a non-system C function f(int,char,float) from my C++
- code?
-
- If you have an individual C function that you want to call, and for some reason
- you don't have or don't want to #include a C header file in which that function
- is declared, you can declare the individual C function in your C code using the
- extern C syntax. Naturally you need to use the full function prototype:
-
- extern "C" void f(int i, char c, float x);
-
- A block of several C functions can be grouped via braces:
-
- extern "C" {
- void f(int i, char c, float x);
- int g(char* s, const char* s2);
- double sqrtOfSumOfSquares(double a, double b);
- }
-
- After this you simply call the function just as if it was a C++ function:
-
- main()
- {
- f(7, 'x', 3.14); // Note: nothing unusual in the call
- }
-
- ==============================================================================
-
- [29.6] How can I create a C++ function f(int,char,float) that is callable by my
- C code?
-
- The C++ compiler must know that f(int,char,float) is to be called by a C
- compiler using the extern C construct[29.3]:
-
- // This is C++ code
-
- // Declare f(int,char,float) using extern C:
- extern "C" void f(int i, char c, float x);
-
- // ...
-
- // Define f(int,char,float) in some C++ module:
- void f(int i, char c, float x)
- {
- // ...
- }
-
- The extern C line tells the compiler that the external information sent to the
- linker should use C calling conventions and name mangling (e.g., preceded by a
- single underscore). Since name overloading isn't supported by C, you can't
- make several overloaded functions simultaneously callable by a C program.
-
- ==============================================================================
-
- [29.7] Why is the linker giving errors for C/C++ functions being called from
- C++/C functions?
-
- If you didn't get your extern C right, you'll sometimes get linker errors
- rather than compiler errors. This is due to the fact that C++ compilers
- usually "mangle" function names (e.g., to support function overloading)
- differently than C compilers.
-
- See the previous two FAQs on how to use extern C.
-
- ==============================================================================
-
- [29.8] How can I pass an object of a C++ class to/from a C function? [UPDATED!]
-
- [Recently added #ifndef FRED_H / #define FRED_H to code (on 1/97).]
-
- Here's an example (for info on extern C, see the previous two FAQs).
-
- Fred.h:
-
- /* This header can be read by both C and C++ compilers */
- #ifndef FRED_H
- #define FRED_H
-
- #ifdef __cplusplus
- class Fred {
- public:
- Fred();
- void wilma(int);
- private:
- int a_;
- };
- #else
- typedef
- struct Fred
- Fred;
- #endif
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- #if defined(__STDC__) || defined(__cplusplus)
- extern void c_function(Fred*); /* ANSI-C prototypes */
- extern Fred* cplusplus_callback_function(Fred*);
- #else
- extern void c_function(); /* K&R style */
- extern Fred* cplusplus_callback_function();
- #endif
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif /*FRED_H*/
-
- Fred.cpp:
-
- // This is C++ code
-
- #include "Fred.h"
-
- Fred::Fred() : a_(0) { }
-
- void Fred::wilma(int a) { }
-
- Fred* cplusplus_callback_function(Fred* fred)
- {
- fred->wilma(123);
- return fred;
- }
-
- main.cpp:
-
- // This is C++ code
-
- #include "Fred.h"
-
- int main()
- {
- Fred fred;
- c_function(&fred);
- return 0;
- }
-
- c-function.c:
-
- /* This is C code */
-
- #include "Fred.h"
-
- void c_function(Fred* fred)
- {
- cplusplus_callback_function(fred);
- }
-
- Passing pointers to C++ objects to/from C functions will fail if you pass and
- get back something that isn't exactly the same pointer. For example, don't
- pass a base class pointer and receive back a derived class pointer, since your
- C compiler won't understand the pointer conversions necessary to handle
- multiple and/or virtual inheritance.
-
- ==============================================================================
-
- [29.9] Can my C function directly access data in an object of a C++ class?
-
- Sometimes.
-
- (For basic info on passing C++ objects to/from C functions, read the previous
- FAQ).
-
- You can safely access a C++ object's data from a C function if the C++ class:
- * Has no virtual[20] functions (including inherited virtual functions)
- * Has all its data in the same access-level section (private/protected/public)
- * Has no fully-contained subobjects with virtual[20] functions
-
- If the C++ class has any base classes at all (or if any fully contained
- subobjects have base classes), accessing the data will technically be
- non-portable, since class layout under inheritance isn't imposed by the
- language. However in practice, all C++ compilers do it the same way: the base
- class object appears first (in left-to-right order in the event of multiple
- inheritance), and member objects follow.
-
- Furthermore, if the class (or any base class) contains any virtual functions,
- almost all C++ compliers put a void* into the object either at the location of
- the first virtual function or at the very beginning of the object. Again, this
- is not required by the language, but it is the way "everyone" does it.
-
- If the class has any virtual base classes, it is even more complicated and less
- portable. One common implementation technique is for objects to contain an
- object of the virtual base class (V) last (regardless of where V shows up as a
- virtual base class in the inheritance hierarchy). The rest of the object's
- parts appear in the normal order. Every derived class that has V as a virtual
- base class actually has a pointer to the V part of the final object.
-
- ==============================================================================
-
- [29.10] Why do I feel like I'm "further from the machine" in C++ as opposed to
- C?
-
- Because you are.
-
- As an OO programming language, C++ allows you to model the problem domain
- itself, which allows you to program in the language of the problem domain
- rather than in the language of the solution domain.
-
- One of C's great strengths is the fact that it has "no hidden mechanism": what
- you see is what you get. You can read a C program and "see" every clock cycle.
- This is not the case in C++; old line C programmers (such as many of us once
- were) are often ambivalent (can you say, "hostile"?) about this feature.
- However after they've made the transition to OO thinking, they often realize
- that although C++ hides some mechanism from the programmer, it also provides a
- level of abstraction and economy of expression which lowers maintenance costs
- without destroying run-time performance.
-
- Naturally you can write bad code in any language; C++ doesn't guarantee any
- particular level of quality, reusability, abstraction, or any other measure of
- "goodness."
-
- C++ doesn't try to make it impossible for bad programmers to write bad
- programs; it enables reasonable developers to create superior software.
-
- ==============================================================================
-
- SECTION [30]: Pointers to member functions
-
-
- [30.1] Is the type of "pointer-to-member-function" different from
- "pointer-to-function"?
-
- Yep.
-
- Consider the following function:
-
- int f(char a, float b);
-
- The type of this function is different depending on whether it is an ordinary
- function or a non-static member function of some class:
- * It's type is "int (*)(char,float)" if an ordinary function
- * It's type is "int (Fred::*)(char,float)" if a non-static member function of
- class Fred
-
- Note: if it's a static member function of class Fred, its type is the same as
- if it was an ordinary function: "int (*)(char,float)".
-
- ==============================================================================
-
- [30.2] How do I pass a pointer to member function to a signal handler, X event
- callback, etc?
-
- Don't.
-
- Because a member function is meaningless without an object to invoke it on, you
- can't do this directly (if The X Windows System was rewritten in C++, it would
- probably pass references to objects around, not just pointers to functions;
- naturally the objects would embody the required function and probably a whole
- lot more).
-
- As a patch for existing software, use a top-level (non-member) function as a
- wrapper which takes an object obtained through some other technique (held in a
- global, perhaps). The top-level function would apply the desired member
- function against the global object.
-
- E.g., suppose you want to call Fred::memberFunction() on interrupt:
-
- class Fred {
- public:
- void memberFunction();
- static void staticMemberFunction(); // A static member function can handle it
- // ...
- };
-
- // Wrapper function uses a global to remember the object:
- Fred* object_which_will_handle_signal;
- void Fred_memberFunction_wrapper()
- {
- object_which_will_handle_signal->memberFunction();
- }
-
- main()
- {
- /* signal(SIGINT, Fred::memberFunction); */ // Can NOT do this
- signal(SIGINT, Fred_memberFunction_wrapper); // OK
- signal(SIGINT, Fred::staticMemberFunction); // Also OK
- }
-
- Note: static member functions do not require an actual object to be invoked, so
- pointers-to-static-member-functions are type compatible with regular
- pointers-to-functions.
-
- ==============================================================================
-
- [30.3] Why do I keep getting compile errors (type mismatch) when I try to use a
- member function as an interrupt service routine?
-
- This is a special case of the previous two questions, therefore read the
- previous two answers first.
-
- Non-static member functions have a hidden parameter that corresponds to the
- this pointer. The this pointer points to the instance data for the object.
- The interrupt hardware/firmware in the system is not capable of providing the
- this pointer argument. You must use "normal" functions (non class members) or
- static member functions as interrupt service routines.
-
- One possible solution is to use a static member as the interrupt service
- routine and have that function look somewhere to find the instance/member pair
- that should be called on interrupt. Thus the effect is that a member function
- is invoked on an interrupt, but for technical reasons you need to call an
- intermediate function first.
-
- ==============================================================================
-
- [30.4] Why am I having trouble taking the address of a C++ function?
-
- This is a corollary to the previous FAQ.
-
- Long answer: In C++, member functions have an implicit parameter which points
- to the object (the this pointer inside the member function). Normal C
- functions can be thought of as having a different calling convention from
- member functions, so the types of their pointers (pointer-to-member-function
- vs. pointer-to-function) are different and incompatible. C++ introduces a new
- type of pointer, called a pointer-to-member, which can be invoked only by
- providing an object.
-
- NOTE: do not attempt to "cast" a pointer-to-member-function into a
- pointer-to-function; the result is undefined and probably disastrous. E.g., a
- pointer-to-member-function is not required to contain the machine address of
- the appropriate function. As was said in the last example, if you have a
- pointer to a regular C function, use either a top-level (non-member) function,
- or a static (class) member function.
-
- ==============================================================================
-
- [30.5] How can I avoid syntax errors when calling a member function using a
- pointer-to-member-function?
-
- Two things: (1) use a typedef, and (2) use a #define macro.
-
- Here's the way you create the typedef:
-
- class Fred {
- public:
- int f(char x, float y);
- int g(char x, float y);
- int h(char x, float y);
- int i(char x, float y);
- // ...
- };
-
- // FredMemberFn points to a member of Fred that takes (char,float)
- typedef int (Fred::*FredMemberFn)(char x, float y);
-
- Here's the way you create the #define macro (normally I dislike #define
- macros[9.3], but this is one of those rare cases where they actually improve
- the readability and writability of your code):
-
- #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))
-
- Here's how you use these features:
-
- void userCode(Fred& fred, FredMemberFn memFn)
- {
- callMemberFunction(fred,memFn)('x', 3.14);
- // Would normally be: (fred.*memFn)('x', 3.14);
- }
-
- I strongly recommend these features. In the real world, member function
- invocations are a lot more complex than the simple example just given, and the
- difference in readability and writability is significant. comp.lang.c++ has
- had to endure hundreds and hundreds of postings from confused programmers who
- couldn't quite get the syntax right. Almost all these errors would have
- vanished had they used these features.
-
- ==============================================================================
-
- [30.6] How do I create and use an array of pointers to member functions?
-
- Use the usual typedef and #define macro[30.5] and you're 90% done.
-
- First, use a typedef:
-
- class Fred {
- public:
- int f(char x, float y);
- int g(char x, float y);
- int h(char x, float y);
- int i(char x, float y);
- // ...
- };
-
- // FredMemberFn points to a member of Fred that takes (char,float)
- typedef int (Fred::*FredMemberFn)(char x, float y);
-
- That makes the array of pointers-to-member-functions straightforward:
-
- FredMemberFn a[4] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i };
-
- Second, use the callMemberFunction macro:
-
- #define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))
-
- That makes calling one of the member functions on object "fred"
- straightforward:
-
- void userCode(Fred& fred, int memberFunctionNum)
- {
- // Assume memberFunctionNum is between 0 and 3 inclusive:
- callMemberFunction(fred, a[memberFunctionNum]) ('x', 3.14);
- }
-
- ==============================================================================
-
- SECTION [31]: Container classes and templates
-
-
- [31.1] How can I make a perl-like associative array in C++?
-
- Use the standard class template map<Key,Val>:
-
- #include <string>
- #include <map>
- #include <iostream>
- using namespace std;
-
- main()
- {
- map<string,int,less<string> > age; // age is a map from string to int
-
- age["Fred"] = 42; // Fred is 42 years old
- age["Barney"] = 37; // Barney is 37
-
- if (todayIsFredsBirthday()) // On Fred's birthday,
- ++ age["Fred"]; // increment Fred's age
-
- cout << "Fred is " << age["Fred"] << " years old\n";
- }
-
- ==============================================================================
-
- [31.2] How can I build a <favorite container> of objects of different types?
-
- You can't, but you can fake it pretty well. In C/C++ all arrays are
- homogeneous (i.e., the elements are all the same type). However, with an extra
- layer of indirection you can give the appearance of a heterogeneous container
- (a heterogeneous container is a container where the contained objects are of
- different types).
-
- There are two cases with heterogeneous containers.
-
- The first case occurs when all objects you want to store in a container are
- publicly derived from a common base class. You can then declare/define your
- container to hold pointers to the base class. You indirectly store a derived
- class object in a container by storing the object's address as an element in
- the container. You can then access objects in the container indirectly through
- the pointers (enjoying polymorphic behavior). If you need to know the exact
- type of the object in the container you can use dynamic_cast<> or typeid().
- You'll probably need the Virtual Constructor Idiom[20.5] to copy a container of
- disparate object types. The downside of this approach is that it makes memory
- management a little more problematic (who "owns" the pointed-to objects? if you
- delete these pointed-to objects when you destroy the container, how can you
- guarantee that no one else has a copy of one of these pointers? if you don't
- delete these pointed-to objects when you destroy the container, how can you be
- sure that someone else will eventually do the deleteing?). It also makes
- copying the container more complex (may actually break the container's copying
- functions since you don't want to copy the pointers, at least not when the
- container "owns" the pointed-to objects).
-
- The second case occurs when the object types are disjoint -- they do not share
- a common base class. The approach here is to use a handle class. The
- container is a container of handle objects (by value or by pointer, your
- choice; by value is easier). Each handle object knows how to "hold on to"
- (i.e. ,maintain a pointer to) one of the objects you want to put in the
- container. You can use either a single handle class with several different
- types of pointers as instance data, or a hierarchy of handle classes that
- shadow the various types you wish to contain (requires the container be of
- handle base class pointers). The downside of this approach is that it opens up
- the handle class(es) to maintenance every time you change the set of types that
- can be contained. The benefit is that you can use the handle class(es) to
- encapsulate most of the ugliness of memory management and object lifetime.
- Thus using handle objects may be beneficial even in the first case.
-
- ==============================================================================
-
- [31.3] How can I insert/access/change elements from a linked
- list/hashtable/etc?
-
- I'll use an "inserting into a linked list" as a prototypical example. It's
- easy to allow insertion at the head and tail of the list, but limiting
- ourselves to these would produce a library that is too weak (a weak library is
- almost worse than no library).
-
- This answer will be a lot to swallow for novice C++'ers, so I'll give a couple
- of options. The first option is easiest; the second and third are better.
-
- 1. Empower the List with a "current location," and member functions such as
- advance(), backup(), atEnd(), atBegin(), getCurrElem(), setCurrElem(Elem),
- insertElem(Elem), and removeElem(). Although this works in small examples,
- the notion of a current position makes it difficult to access elements at
- two or more positions within the list (e.g., "for all pairs x,y do the
- following...").
-
- 2. Remove the above member functions from List itself, and move them to a
- separate class, ListPosition. ListPosition would act as a "current
- position" within a list. This allows multiple positions within the same
- list. ListPosition would be a friend[14] of class List, so List can hide
- its innards from the outside world (else the innards of List would have to
- be publicized via public member functions in List). Note: ListPosition can
- use operator overloading for things like advance() and backup(), since
- operator overloading is syntactic sugar for normal member functions.
-
- 3. Consider the entire iteration as an atomic event, and create a class
- template to embodies this event. This enhances performance by allowing the
- public access member functions (which may be virtual[20] functions) to be
- avoided during the inner loop. Unfortunately you get extra object code in
- the application, since templates gain speed by duplicating code. For more,
- see [Koenig, "Templates as interfaces," JOOP, 4, 5 (Sept 91)], and
- [Stroustrup, "The C++ Programming Language Second Edition," under
- "Comparator"].
-
- ==============================================================================
-
- [31.4] What's the idea behind templates?
-
- A template is a cookie-cutter that specifies how to cut cookies that all look
- pretty much the same (although the cookies can be made of various kinds of
- dough, they'll all have the same basic shape). In the same way, a class
- template is a cookie cutter for a description of how to build a family of
- classes that all look basically the same, and a function template describes how
- to build a family of similar looking functions.
-
- Class templates are often used to build type safe containers (although this
- only scratches the surface for how they can be used).
-
- ==============================================================================
-
- [31.5] What's the syntax / semantics for a "function template"?
-
- Consider this function that swaps its two integer arguments:
-
- void swap(int& x, int& y)
- {
- int tmp = x;
- x = y;
- y = tmp;
- }
-
- If we also had to swap floats, longs, Strings, Sets, and FileSystems, we'd get
- pretty tired of coding lines that look almost identical except for the type.
- Mindless repetition is an ideal job for a computer, hence a function template:
-
- template<class T>
- void swap(T& x, T& y)
- {
- T tmp = x;
- x = y;
- y = tmp;
- }
-
- Every time we used swap() with a given pair of types, the compiler will go to
- the above definition and will create yet another "template function" as an
- instantiation of the above. E.g.,
-
- main()
- {
- int i,j; /*...*/ swap(i,j); // Instantiates a swap for int
- float a,b; /*...*/ swap(a,b); // Instantiates a swap for float
- char c,d; /*...*/ swap(c,d); // Instantiates a swap for char
- String s,t; /*...*/ swap(s,t); // Instantiates a swap for String
- }
-
- Note: A "template function" is the instantiation of a "function template".
-
- ==============================================================================
-
- [31.6] What's the syntax / semantics for a "class template"?
-
- Consider a container class Array that acts like an array of integers:
-
- // This would go into a header file such as "Array.h"
- class Array {
- public:
- Array(int len=10) : len_(len), data_(new int[len]) { }
- ~Array() { delete [] data_; }
- int len() const { return len_; }
- const int& operator[](int i) const { return data_[check(i)]; }
- int& operator[](int i) { return data_[check(i)]; }
- Array(const Array&);
- Array& operator= (const Array&);
- private:
- int len_;
- int* data_;
- int check(int i) const
- { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_);
- return i; }
- };
-
- Just as with swap() above, repeating the above over and over for Array of
- float, of char, of String, of Array-of-String, etc, will become tedious.
-
- // This would go into a header file such as "Array.h"
- template<class T>
- class Array {
- public:
- Array(int len=10) : len_(len), data_(new T[len]) { }
- ~Array() { delete [] data_; }
- int len() const { return len_; }
- const T& operator[](int i) const { return data_[check(i)]; }
- T& operator[](int i) { return data_[check(i)]; }
- Array(const Array<T>&);
- Array<T>& operator= (const Array<T>&);
- private:
- int len_;
- T* data_;
- int check(int i) const
- { if (i < 0 || i >= len_) throw BoundsViol("Array", i, len_);
- return i; }
- };
-
- Unlike template functions, template classes (instantiations of class templates)
- need to be explicit about the parameters over which they are instantiating:
-
- main()
- {
- Array<int> ai;
- Array<float> af;
- Array<char*> ac;
- Array<String> as;
- Array< Array<int> > aai;
- }
-
- Note the space between the two >'s in the last example. Without this space,
- the compiler would see a >> (right-shift) token instead of two >'s.
-
- ==============================================================================
-
- [31.7] What is a "parameterized type"?
-
- Another way to say, "class templates."
-
- A parameterized type is a type that is parameterized over another type or some
- value. List<int> is a type (List) parameterized over another type (int).
-
- ==============================================================================
-
- [31.8] What is "genericity"?
-
- Yet another way to say, "class templates."
-
- Not to be confused with "generality" (which just means avoiding solutions which
- are overly specific), "genericity" means class templates.
-
- ==============================================================================
-
- SECTION [32]: Class libraries
-
-
- [32.1] Where can I get a copy of "STL"?
-
- "STL" is the "Standard Templates Library". You can get a copy from:
- * An STL site: ftp://ftp.cs.rpi.edu/pub/stl
- * STL HP official site: ftp://butler.hpl.hp.com/stl/
- * Mirror site in Europe: http://www.maths.warwick.ac.uk/ftp/mirrors/c++/stl/
- * STL code alternate: ftp://ftp.cs.rpi.edu/stl
- * STL code + examples: http://www.cs.rpi.edu/~musser/stl.html
-
- STL hacks for GCC-2.6.3 are part of the GNU libg++ package 2.6.2.1 or later
- (and they may be in an earlier version as well). Thanks to Mike Lindner.
-
- ==============================================================================
-
- [32.2] How can I find a Fred object in an STL container of Fred* such as
- vector<Fred*>?
-
- STL functions such as std::find_if() help you find a T element in a container
- of T's. But if you have a container of pointers such as vector<Fred*>, these
- functions will enable you to find an element that matches a given Fred*
- pointer, but they don't let you find an element that matches a given Fred
- object.
-
- The solution is to use an optional parameter that specifies the "match"
- function. The following class template lets you compare the objects on the
- other end of the dereferenced pointers.
-
- template<class T>
- class DereferencedEqual {
- public:
- DereferencedEqual(const T* p) : p_(p) { }
- bool operator== (const T* p2) const { return *p == *p2; }
- private:
- const T* p_;
- };
-
- Now you can use this template to find an appropriate Fred object:
-
- void userCode(vector<Fred*> v, const Fred& match)
- {
- find_if(v.begin(), v.end(), DereferencedEqual<Fred>(&match));
- // ...
- }
-
- ==============================================================================
-
- [32.3] Where can I get help on how to use STL?
-
- Kenny Zalewski's STL guide: http://www.cs.rpi.edu/projects/STL/htdocs/stl.html
-
- Dave Musser's STL guide: http://www.cs.rpi.edu/~musser/stl.html
-
- Mumit's STL Newbie's guide:
- http://www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html
-
- ==============================================================================
-
- [32.4] How can you tell if you have a dynamically typed C++ class library?
-
- * Hint #1: when everything is derived from a single root class, usually
- Object.
- * Hint #2: when the container classes (List, Stack, Set, etc) are
- non-templates.
- * Hint #3: when the container classes (List, Stack, Set, etc) insert/extract
- elements as pointers to Object. This lets you put an Apple into such a
- container, but when you get it out, the compiler knows only that it is
- derived from Object, so you have to use a pointer cast to convert it back to
- an Apple*; and you better pray a lot that it really is an Apple, cause your
- blood is on your own head).
-
- You can make the pointer cast "safe" by using dynamic_cast, but this dynamic
- testing is just that: dynamic. This coding style is the essence of dynamic
- typing in C++. You call a function that says "convert this Object into an
- Apple or give me NULL if its not an Apple," and you've got dynamic typing: you
- don't know what will happen until run-time.
-
- When you use templates to implement your containers, the C++ compiler can
- statically validate 90+% of an application's typing information (the figure
- "90+%" is apocryphal; some claim they always get 100%, those who need
- persistence[34.4] get something less than 100% static type checking). The
- point is: C++ gets genericity from templates, not from inheritance.
-
- ==============================================================================
-
- [32.5] What is the NIHCL? Where can I get it?
-
- NIHCL stands for "National-Institute-of-Health's-class-library." It can be
- acquired via ftp://128.231.128.7/pub/NIHCL/nihcl-3.0.tar.Z
-
- NIHCL (some people pronounce it "N-I-H-C-L," others pronounce it like "nickel")
- is a C++ translation of the Smalltalk class library[32.4]. There are some ways
- where NIHCL's use of dynamic typing helps (e.g., persistent[34.4] objects).
- There are also places where its use of dynamic typing creates tension[27.3]
- with the static typing of the C++ language.
-
- ==============================================================================
-
- [32.6] Where can I ftp the code that accompanies "Numerical Recipes"?
-
- This software is sold and therefore it would be illegal to provide it on the
- net. However, it's only about $30.
-
- ==============================================================================
-
- [32.7] Why is my executable so large?
-
- Many people are surprised by how big executables are, especially if the source
- code is trivial. For example, a simple "hello world" program can generate an
- executable that is larger than most people expect (40+K bytes).
-
- One reason executables can be large is that portions of the C++ runtime library
- gets linked with your program. How much gets linked in depends on how much of
- it you are using, and on how the implementer split up the library into pieces.
- For example, the <iostream.h> library is quite large, and consists of numerous
- classes and virtual[20] functions. Using any part of it might pull in nearly
- all of the <iostream.h> code as a result of the interdependencies.
-
- You might be able to make your program smaller by using a dynamically-linked
- version of the library instead of the static version.
-
- You have to consult your compiler manuals or the vendor's technical support for
- a more detailed answer.
-
- ==============================================================================
-
- [32.8] Where can I get tons and tons of more information on C++ class
- libraries?
-
- The C++ Libraries FAQ is maintained by Nikki Locke cpplibs@trmphrst.demon.co.uk
- and is available at http://www.trmphrst.demon.co.uk/cpplibs1.html
-
- ==============================================================================
-
- SECTION [33]: Compiler dependencies
-
-
- [33.1] How do I display text in the status bar using MFC? [NEW!]
-
- [Recently created with the help of Paul Ganney (on 11/96).]
-
- Use the following code snipped:
-
- CString s = "Text";
- CStatusBar* p =
- (CStatusBar*)AfxGetApp()->m_pMainWnd->GetDescendantWindow(AFX_IDW_STATUS_BAR);
- p->SetPaneText(1, s);
-
- This works with MFC v.1.00 which hopefully means it will work with other
- versions as well.
-
- ==============================================================================
-
- [33.2] How can I decompile an executable program back into C++ source code?
-
- You gotta be kidding, right?
-
- Here are a few of the many reasons this is not even remotely feasible:
- * What makes you think the program was written in C++ to begin with?
- * Even if you are sure it was originally written (at least partially) in C++,
- which one of the gazillion C++ compilers produced it?
- * Even if you know the compiler, which particular version of the compiler was
- used?
- * Even if you know the compiler's manufacturer and version number, what
- compile-time options were used?
- * Even if you know the compiler's manufacturer and version number and
- compile-time options, what third party libraries were linked-in, and what
- was their version?
- * Even if you know all that stuff, most executables have had their debugging
- information stripped out, so the resulting decompiled code will be totally
- unreadable.
- * Even if you know everything about the compiler, manufacturer, version
- number, compile-time options, third party libraries, and debugging
- information, the cost of writing a decompiler that works with even one
- particular compiler and has even a modest success rate at generating code
- would be a monumental effort -- on the par with writing the compiler itself
- from scratch.
-
- But the biggest question is not how you can decompile someone's code, but why
- do you want to do this? If you're trying to reverse-engineer someone elses
- code, shame on you; go find honest work. If you're trying to recover from
- losing your own source, the best suggestion I have is to make better backups
- next time.
-
- ==============================================================================
-
- [33.3] Where can I get information about the C++ compiler from {Borland, IBM,
- Microsoft, Semantic, Sun, etc.}? [UPDATED!]
-
- [Recently added HP C++ (on 1/97).]
-
- In alphabetical order by vendor name:
- * Borland C++ 5.0 FAQs: http://www.mdex.net/~kentr/bc50faq.htm
- * HP C++: [please let me know; thanks; (cline@parashift.com)]
- * IBM VisualAge C++: http://www.software.ibm.com/ad/cset/
- * Metrowerks C++: http://metrowerks.com or http://www.metrowerks.com
- * Microsoft Visual C++: a tutorial [please let me know about other links;
- thanks; (cline@parashift.com)]
- * Silicon Graphics C++:
- http://www.sgi.com/Products/DevMagic/products/cplusplus.html
- * Sun C++: [please let me know; thanks; (cline@parashift.com)]
- * Symantec C++: http://www.symantec.com/lit/dev/
- * Watcom C++: http://www.powersoft.com/products/languages/watccpl.html
-
- [If anyone has other suggestions that should go into this list, please let me
- know; thanks; (cline@parashift.com)].
-
- ==============================================================================
-
- [33.4] How do compilers use "over-allocation" to remember the number of
- elements in an allocated array?
-
- Recall that when you delete[] an array, the runtime system magically knows how
- many destructors to run[16.13]. This FAQ describes a technique used by some
- C++ compilers to do this (the other common technique is to use an associative
- array[33.5]).
-
- If the compiler uses the "over-allocation" technique, the code for
- p = new Fred[n] looks something like the following. Note that WORDSIZE is an
- imaginary machine-dependent constant that is at least sizeof(size_t), possibly
- rounded up for any alignment constraints. On many machines, this constant will
- have a value of 4 or 8. It is not a real C++ identifier that will be defined
- for your compiler.
-
- // Original code: Fred* p = new Fred[n];
- char* tmp = (char*) operator new[] (WORDSIZE + n * sizeof(Fred));
- Fred* p = (Fred*) (tmp + WORDSIZE);
- *(size_t*)tmp = n;
- size_t i;
- try {
- for (i = 0; i < n; ++i)
- new(p + i) Fred(); // Placement new[11.10]
- } catch (...) {
- while (i-- != 0)
- (p + i)->~Fred(); // Explicit call to the destructor[11.10]
- operator delete[] ((char*)p - WORDSIZE);
- throw;
- }
-
- Then the delete[] p statement becomes:
-
- // Original code: delete[] p;
- size_t n = * (size_t*) ((char*)p - WORDSIZE);
- while (n-- != 0)
- (p + n)->~Fred();
- operator delete[] ((char*)p - WORDSIZE);
-
- Note that the address passed to operator delete[] is not the same as p.
-
- Compared to the associative array technique[33.5], this technique is faster,
- but more sensitive to the problem of programmers saying delete p rather than
- delete[] p. For example, if you make a programming error by saying delete p
- where you should have said delete[] p, the address that is passed to
- operator delete(void*) is not the address of any valid heap allocation. This
- will probably corrupt the heap. Bang! You're dead!
-
- ==============================================================================
-
- [33.5] How do compilers use an "associative array" to remember the number of
- elements in an allocated array?
-
- Recall that when you delete[] an array, the runtime system magically knows how
- many destructors to run[16.13]. This FAQ describes a technique used by some
- C++ compilers to do this (the other common technique is to
- over-allocate[33.4]).
-
- If the compiler uses the associative array technique, the code for
- p = new Fred[n] looks something like this (where arrayLengthAssociation is the
- imaginary name of a hidden, global associative array that maps from void* to
- "size_t"):
-
- // Original code: Fred* p = new Fred[n];
- Fred* p = (Fred*) operator new[] (n * sizeof(Fred));
- size_t i;
- try {
- for (i = 0; i < n; ++i)
- new(p + i) Fred(); // Placement new[11.10]
- } catch (...) {
- while (i-- != 0)
- (p + i)->~Fred(); // Explicit call to the destructor[11.10]
- operator delete[] (p);
- throw;
- }
- arrayLengthAssociation.insert(p, n);
-
- Then the delete[] p statement becomes:
-
- // Original code: delete[] p;
- size_t n = arrayLengthAssociation.lookup(p);
- while (n-- != 0)
- (p + n)->~Fred();
- operator delete[] (p);
-
- Cfront uses this technique (it uses an AVL tree to implement the associative
- array).
-
- Compared to the over-allocation technique[33.4], the associative array
- technique is slower, but less sensitive to the problem of programmers saying
- delete p rather than delete[] p. For example, if you make a programming error
- by saying delete p where you should have said delete[] p, only the first Fred
- in the array gets destructed, but the heap may survive (unless you've replaced
- operator delete[] with something that doesn't simply call operator delete, or
- unless the destructors for the other Fred objects were necessary).
-
- ==============================================================================
-
- [33.6] If name mangling was standardized, could I link code compiled with
- compilers from different compiler vendors? [UPDATED!]
-
- [Recently reworded and added v-table and v-pointer references[20.3] (on
- 11/96).]
-
- Short answer: Probably not.
-
- In other words, some people would like to see name mangling standards
- incorporated into the proposed C++ ANSI standards in an attempt to avoiding
- having to purchase different versions of class libraries for different compiler
- vendors. However name mangling differences are one of the smallest differences
- between implementations, even on the same platform.
-
- Here is a partial list of other differences:
- * Number and type of hidden arguments to member functions.
- - is this handled specially?
- - where is the return-by-value pointer passed?
- * Assuming a v-table[20.3] is used:
- - what is its contents and layout?
- - where/how is the adjustment to this made for multiple and/or virtual
- inheritance?
- * How are classes laid out, including:
- - location of base classes?
- - handling of virtual base classes?
- - location of v-pointers[20.3], if they are used at all?
- * Calling convention for functions, including:
- - where are the actual parameters placed?
- - in what order are the actual parameters passed?
- - how are registers saved?
- - where does the return value go?
- - does caller or callee pop the stack after the call?
- - special rules for passing or returning structs or doubles?
- - special rules for saving registers when calling leaf functions?
- * How is the run-time-type-identification laid out?
- * How does the runtime exception handling system know which local objects need
- to be destructed during an exception throw?
-
- ==============================================================================
-
- [33.7] GNU C++ (g++) produces big executables for tiny programs; Why?
-
- libg++ (the library used by g++) was probably compiled with debug info (-g).
- On some machines, recompiling libg++ without debugging can save lots of disk
- space (approximately 1 MB; the down-side: you'll be unable to trace into libg++
- calls). Merely strip-ping the executable doesn't reclaim as much as
- recompiling without -g followed by subsequent strip-ping the resultant a.out's.
-
- Use size a.out to see how big the program code and data segments really are,
- rather than ls -s a.out which includes the symbol table.
-
- ==============================================================================
-
- [33.8] Is there a yacc-able C++ grammar?
-
- There used to be a yacc grammar that was pretty close to C++. As far as I am
- aware, it has not kept up with the evolving C++ standard. For example, the
- grammar doesn't handle templates, "exceptions", nor
- run-time-type-identification, and it deviates from the rest of the language in
- some subtle ways.
-
- It is available at http://srawgw.sra.co.jp/.a/pub/cmd/c++grammar2.0.tar.gz
-
- ==============================================================================
-
- [33.9] What is C++ 1.2? 2.0? 2.1? 3.0?
-
- These are not versions of the language, but rather versions of cfront, which
- was the original C++ translator implemented by AT&T. It has become generally
- accepted to use these version numbers as if they were versions of the language
- itself.
-
- Very roughly speaking, these are the major features:
- * 2.0 includes multiple/virtual inheritance and pure virtual[22.4] functions
- * 2.1 includes semi-nested classes and delete[] pointerToArray
- * 3.0 includes fully-nested classes, templates and i++ vs. ++i
- * 4.0 will include exceptions
-
- ==============================================================================
-
- SECTION [34]: Miscellaneous technical issues
-
-
- [34.1] Why can't the compiler find my header file in #include "c:\test.hpp" ?
-
- Because "\t" is a tab character.
-
- You should use forward slashes ("/") rather than backslashes ("\") in your
- #include filenames, even on an operating system that uses backslashes such as
- DOS, Windows, OS/2, etc. For example:
-
- #if 1
- #include "/version/next/alpha/beta/test.hpp" // RIGHT!
- #else
- #include "\version\next\alpha\beta\test.hpp" // WRONG!
- #endif
-
- Note that you should use forward slashes ("/") on all your filenames[15.10],
- not just on your #include files.
-
- ==============================================================================
-
- [34.2] Does C++ have new scoping rules for for loops?
-
- Yep.
-
- The following code used to be legal, but not any more, sinc i's scope is now
- inside the for loop only:
-
- for (int i = 0; i < 10; ++i) {
- // ...
- if ( /* something wierd */ )
- break;
- // ...
- }
-
- if (i != 10) {
- // We exited the loop early; handle this situation separately
- // ...
- }
-
- Unless you use a for loop variable after the for loop, the new scoping rule
- won't break your code. If it does break your code, in most cases the compiler
- will give you a compile-time error message such as "Variable i is not in
- scope".
-
- Unfortunately it is possible that this new rule will silently cause your code
- to do the wrong thing. For example, if you have a global variable i, the above
- code if (i != 10) silently change in meaning from the for loop variable i under
- the old rule to the global variable i under the new rule. This is not good.
- If you're concerned, you should check with your compiler to see if it has some
- option that forces it to use the old rules with your old code.
-
- Note: You should avoid having the same variable name nested scopes, such as a
- global i and a local i. In fact, you should avoid globals althogether whenever
- you can. If you abided by these coding standards in your old code, you won't
- be hurt by the new scoping rules for for loop variables.
-
- ==============================================================================
-
- [34.3] Why can't I overload a function by its return type?
-
- If you declare both char f() and float f(), the compiler gives you an error
- message, since calling simply f() would be ambiguous.
-
- ==============================================================================
-
- [34.4] What is "persistence"? What is a "persistent object"?
-
- A persistent object can live after the program which created it has stopped.
- Persistent objects can even outlive different versions of the creating program,
- can outlive the disk system, the operating system, or even the hardware on
- which the OS was running when they were created.
-
- The challenge with persistent objects is to effectively store their member
- function code out on secondary storage along with their data bits (and the data
- bits and member function code of all member objects, and of all their member
- objects and base classes, etc). This is non-trivial when you have to do it
- yourself. In C++, you have to do it yourself. C++/OO databases can help hide
- the mechanism for all this.
-
- ==============================================================================
-
- [34.5] Why is floating point so inaccurate? Why doesn't this print 0.43?
-
- #include <iostream.h>
-
- main()
- {
- float a = 1000.43;
- float b = 1000.0;
- cout << a - b << '\n';
- }
-
- (On one C++ implementation, this prints 0.429993)
-
- Disclaimer: Frustration with rounding/truncation/approximation isn't really a
- C++ issue; it's a computer science issue. However, people keep asking about it
- on comp.lang.c++, so what follows is a nominal answer.
-
- Answer: Floating point is an approximation. The IEEE standard for 32 bit float
- supports 1 bit of sign, 8 bits of exponent, and 23 bits of mantissa. Since a
- normalized binary-point mantissa always has the form 1.xxxxx... the leading 1
- is dropped and you get effectively 24 bits of mantissa. The number 1000.43
- (and many, many others) is not exactly representable in float or double format.
- 1000.43 is actually represented as the following bitpattern (the "s" shows the
- position of the sign bit, the "e"s show the positions of the exponent bits, and
- the "m"s show the positions of the mantissa bits):
-
- seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
- 01000100011110100001101110000101
-
- The shifted mantissa is 1111101000.01101110000101 or 1000 + 7045/16384. The
- fractional part is 0.429992675781. With 24 bits of mantissa you only get about
- 1 part in 16M of precision for float. The double type provides more precision
- (53 bits of mantissa).
-
- ==============================================================================
-
- SECTION [35]: Miscellaneous environmental issues
-
-
- [35.1] Is there a TeX or LaTeX macro that fixes the spacing on "C++"?
-
- Yes, here are three (the first prevents line breaks between the C and "++"):
-
- \def\CC{{C\nolinebreak[4]\hspace{-.05em}\raisebox{.4ex}{\tiny\bf ++}}}
-
- \def\CC{C\raise.22ex\hbox{{\footnotesize +}}\raise.22ex\hbox{\footnotesize +}}
-
- \def\CC{{C\hspace{-.05em}\raisebox{.4ex}{\tiny\bf ++}}}
-
- ==============================================================================
-
- [35.2] Are there any pretty-printers that reformat C++ source code?
-
- In alphabetical order:
- * C++2LaTeX is a LaTeX pretty printer. It is available via ftp from
- ftp://ftp.germany.eu.net/pub/packages/TeX/support/pretty/C++2LaTeX-3.02.tar.gz
- * C-Clearly by V Communications, Inc. is a Windows program that comes with
- standard formatting templates and also allows you to customize your own.
- http://www.v-com.com/
- * GNU indent program may help. It's somewhere on ftp://www.cygnus.com [Note:
- If anyone has a better URL, please let me know (cline@parashift.com).]
- * tgrind is a Unix based pretty printer. It usually comes with the public
- distribution of TeX and LaTeX in the directory
- "...tex82/contrib/van/tgrind". A more up-to-date version of tgrind by Jerry
- Leichter can be found on: ftp://venus.ycc.yale.edu/pub in [.TGRIND]. [Note:
- If anyone has an updated URL for tgrind, please let me know
- (cline@parashift.com).]
-
- ==============================================================================
-
- [35.3] Is there a C++-mode for GNU emacs? If so, where can I get it?
-
- Yes, there is a C++-mode for GNU emacs.
-
- The latest and greatest version of C++-mode (and C-mode) is implemented in the
- file cc-mode.el. It is an extension of Detlef and Clamen's version. A version
- is included with emacs. Newer version are available from the elisp archives.
-
- ==============================================================================
-
- [35.4] Where can I get OS-specific questions answered (e.g., BC++, DOS,
- Windows, etc)? [UPDATED!]
-
- [Recently added Borland C++ URLs thanks to Simon Edlund (on 1/97).]
-
- See one of the following:
- * MS-DOS issues: comp.os.msdos.programmer
- * MS-Windows issues: comp.windows.ms.programmer
- * Unix issues: comp.unix.programmer
- * Borland C++ issues (e.g., OWL, BC++ compiler bugs, general C++ concepts,
- windows programming):
- - Using your Web browser: http://www.cs.rpi.edu/~wiseb/owl-list/
- - To get on the mailing list: send an e-mail message with the word
- "SUBSCRIBE" in the Subject: line to majordomo@netlab.cs.rpi.edu
- - To get the FAQ:
- ftp://ftp.netlab.cs.rpi.edu/pub/lists/owl-list-faq/drafts/owl_faq.hlp
-
- ==============================================================================
-
- [35.5] Why does my DOS C++ program says "Sorry: floating point code not
- linked"?
-
- The compiler attempts to save space in the executable by not including the
- float-to-string format conversion routines unless they are necessary, but
- sometimes it guesses wrong, and gives you the above error message. You can fix
- this by (1) using <iostream.h> instead of <stdio.h>, or (2) by including the
- following function somewhere in your compilation (but don't call it!):
-
- static void dummyfloat(float *x) { float y; dummyfloat(&y); }
-
- See FAQ on stream I/O for more reasons to use <iostream.h> vs. <stdio.h>.
-
- ==============================================================================
-
- [35.6] Why does my BC++ Windows app crash when I'm not running the BC45 IDE?
-
- If you're using BC++ for a Windows app, and it works OK as long as you have the
- BC45 IDE running, but when the BC45 IDE is shut down you get an exception
- during the creation of a window, then add the following line of code to the
- InitMainWindow() member function of your application
- (YourApp::InitMainWindow()):
-
- EnableBWCC(TRUE);
-
- ==============================================================================
-
-